﻿using System;
using System.Threading;

namespace Dapper.Sharding.CAP
{
    public class DistributedTransactionCap : DistributedTransaction
    {
        public TranMessage TranMsg { get; } = new TranMessage();

        private ClientCap ClientCap { get; }

        private int TimeOutSeconds { get; set; }

        internal AutoResetEvent autoResetEvent { get; set; }

        public DistributedTransactionCap(ClientCap clientCap, string token, string serverActor, string tranName = null, int timeOutSeconds = 5)
        {
            ClientCap = clientCap;
            TranMsg.Token = token;
            TranMsg.TranId = ShardingFactory.NextObjectId();
            TranMsg.TranName = tranName;
            TranMsg.ServerActor = serverActor;
            TimeOutSeconds = timeOutSeconds;
        }

        public new void Commit()
        {
            autoResetEvent = new AutoResetEvent(false);
            TranMsg.TranPreCommit = true; //标记预提交为true
            ClientCap.dict.TryAdd(TranMsg.TranId, this);
            ClientCap.Tell(TranMsg.ServerActor, TranMsg); //告诉server我已经预提交了
            var ok = autoResetEvent.WaitOne(TimeOutSeconds * 1000);//开始阻塞等待
            ClientCap.dict.TryRemove(TranMsg.TranId, out var _);
            try
            {
                autoResetEvent.Dispose();
                autoResetEvent = null;
            }
            catch { }

            if (!ok && TranMsg.ClientStatus == 0)
            {
                throw new Exception($"time out after {TimeOutSeconds} seconds,tran rollback.");
            }
            else if (TranMsg.ClientStatus == 1)
            {
                base.Commit();
            }
            else
            {
                throw new Exception($"cap tran rollback.");
            }
        }

        public new void Rollback()
        {
            if (autoResetEvent != null)
            {
                try
                {
                    autoResetEvent.Dispose();
                }
                catch { }
            }
            if (TranMsg.ClientStatus == 0)
            {
                TranMsg.TranPreCommit = false; //标记自己预提交失败了
                ClientCap.Tell(TranMsg.ServerActor, TranMsg); //告诉server我已经回滚了
            }
            base.Rollback();
        }
    }
}
